package org.example.codeservce.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ArrayUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.example.codeservce.api.TongYiCannel;
import org.example.codeservce.entity.Order;
import org.example.codeservce.pojo.vo.PromptBigVo;
import org.example.codeservce.pojo.vo.PromptVo;
import org.example.codeservce.service.CodeAnalysis;
import org.example.codeservce.service.OrderService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * author: Li Qing
 * date: 2024/5/10 15:31
 * description:
 */
@Service("SPRING_BOOT_BEAN")
@Slf4j
public class CodeAnalysisSpringBoot implements CodeAnalysis
{

    @Resource
    private ThreadPoolExecutor poolExecutor;

    @Resource
    private OrderService orderService;

    @SneakyThrows
    @Override
    public PromptBigVo analysisCodeStructure(String unzipFile, Order order)
    {
        List<CompletableFuture<Void>> futures = new ArrayList<>();
        modifyTheProgress(order.getId(), "10%");
        log.info("解析源码百分之10" + "订单：" + order.getOrderNo());
        Map<String, File> map = new HashMap<>();
        analysisPackage(new File(unzipFile), map);
        if (CollectionUtil.isEmpty(map))
        {
            throw new RuntimeException("springboot项目源码不完整！");
        }
        log.info("解析源码百分之20" + "订单：" + order.getOrderNo());
        modifyTheProgress(order.getId(), "20%");
        Map<String, File> javaMap = new HashMap<>();
        //解析Java代码
        File javaFile = map.get("java");
        analysisFindPackage(javaFile, javaMap);
        Set<String> keys = javaMap.keySet();
        AtomicReference<Integer> progressAto = new AtomicReference<>(60);
        modifyTheProgress(order.getId(), "60%");
        log.info("解析源码百分之60" + "订单：" + order.getOrderNo());
        //拿到包的介绍
        AtomicReference<Map<File, String>> mapRest = new AtomicReference<>(new HashMap<>());
        futures.add(CompletableFuture.runAsync(() ->
                                               {
                                                   mapRest.set(getPageInfo(keys, javaMap, poolExecutor));
                                                   log.info("包解析完成" + "订单：" + order.getOrderNo());
                                                   if (CollectionUtil.isEmpty(mapRest.get()))
                                                   {
                                                       throw new RuntimeException("springboot项目源码不完整！");
                                                   }
                                                   progressAto.set(progressAto.get() + 10);
                                                   log.info("解析源码百分之" + progressAto.get());
                                                   modifyTheProgress(order.getId(), progressAto.get() + "%");
                                               }, poolExecutor));

        //分析类
        AtomicReference<Map<String, List<PromptVo>>> classPromptVo = new AtomicReference<>(new HashMap<>());
        futures.add(CompletableFuture.runAsync(() ->
                                               {
                                                   classPromptVo.set(analyzeTheClass(javaMap, poolExecutor));
                                                   log.info("分析类分析完成" + "订单：" + order.getOrderNo());
                                                   if (CollectionUtil.isEmpty(classPromptVo.get()))
                                                   {
                                                       throw new RuntimeException("未读取到springboot包下的类！");
                                                   }
                                                   progressAto.set(progressAto.get() + 10);
                                                   log.info("解析源码百分之" + progressAto.get());
                                                   modifyTheProgress(order.getId(), progressAto.get() + "%");
                                               }, poolExecutor));

        //分析 配置文件
        AtomicReference<List<PromptVo>> resourcesPromptVo = new AtomicReference<>(new ArrayList<>());
        futures.add(CompletableFuture.runAsync(() ->
                                               {
                                                   resourcesPromptVo.set(analyzeTheProfile(map.get("resources"), poolExecutor));
                                                   log.info("分析配置文件分析完成" + "订单：" + order.getOrderNo());
                                                   if (CollectionUtil.isEmpty(resourcesPromptVo.get()))
                                                   {
                                                       log.error("未读取到springboot 配置文件！");
                                                   }
                                                   progressAto.set(progressAto.get() + 10);
                                                   log.info("解析源码百分之" + progressAto.get());
                                                   modifyTheProgress(order.getId(), progressAto.get() + "%");
                                               }, poolExecutor));

        //分析 pom.xml 解析框架
        AtomicReference<PromptVo> pomPromptVo = new AtomicReference<>();
        futures.add(CompletableFuture.runAsync(() ->
                                               {
                                                   pomPromptVo.set(analyzeThePom(map.get("pom.xml"), poolExecutor));
                                                   log.info("分析pom.xml分析完成" + "订单：" + order.getOrderNo());
                                                   if (pomPromptVo.get() == null)
                                                   {
                                                       log.error("未读取到springboot的pom.xml！");
                                                   }
                                                   progressAto.set(progressAto.get() + 10);
                                                   log.info("解析源码百分之" + progressAto.get());
                                                   modifyTheProgress(order.getId(), progressAto.get() + "%");
                                               }, poolExecutor));

        futures.forEach(CompletableFuture::join);

        return
                PromptBigVo.builder()
                        .resourcesPromptVo(resourcesPromptVo.get())
                        .classPromptVo(classPromptVo.get())
                        .pageinfos(mapRest.get())
                        .build();
    }

    /**
     * pom.xml 的解析
     *
     * @param file
     * @param poolExecutor
     * @return
     */
    @SneakyThrows
    private PromptVo analyzeThePom(File file, ThreadPoolExecutor poolExecutor)
    {
        if (file == null)
        {
            return null;
        }
        String content = FileUtil.readUtf8String(file.getAbsolutePath()) + pomPrompt;
        TongYiCannel tongYiCannel = new TongYiCannel();
        tongYiCannel.setPrompt(content);
        String call = tongYiCannel.call();
        return PromptVo.builder()
                .file(file)
                .fileName(file.getName())
                .prompt(content)
                .resultPrompt(call)
                .build();
    }

    private void modifyTheProgress(Integer id, String progress)
    {
        orderService.updateById(new Order()
                                        .setId(id)
                                        .setProgress(progress)
        );
    }

    /**
     * 拿到包的介绍 方法名
     *
     * @param keys
     * @param javaMap
     * @param executor
     * @return
     * @throws InterruptedException
     * @throws ExecutionException
     */
    private static Map<File, String> getPageInfo(Set<String> keys, Map<String, File> javaMap, ExecutorService executor)
    {
        Map<File, String> mapRest = new HashMap<>();
        List<CompletableFuture<Void>> futures = keys.parallelStream()
                .map(key -> CompletableFuture.runAsync(() ->
                                                       {
                                                           try
                                                           {
                                                               TongYiCannel tongYiCannel = createTongYiCannel(key, javaMap);
                                                               String rest = tongYiCannel.call();
                                                               mapRest.put(javaMap.get(key), rest);
                                                               // log.info("提问:" + key + "回答：" + rest);
                                                               // log.info("提问：{}，回答：{}", key, rest);
                                                           } catch (Exception e)
                                                           {
                                                               // 处理异常情况
                                                               //   System.err.println("处理 key: " + key + " 时出现异常: " + e.getMessage());
                                                               // log.error("处理 key: {} 时出现异常: {}", key, e.getMessage());
                                                           }
                                                       }, executor))
                .collect(Collectors.toList());

        futures.forEach(CompletableFuture::join);
        return mapRest;
    }

    private static TongYiCannel createTongYiCannel(String key, Map<String, File> javaMap)
    {
        TongYiCannel tongYiCannel = new TongYiCannel();
        tongYiCannel.setPrompt(javaMap.get(key).getPath() + pagePrompt);
        return tongYiCannel;
    }


    /**
     * 分析类
     *
     * @param mapRest
     * @param executor
     * @return
     */
    @SneakyThrows
    private Map<String, List<PromptVo>> analyzeTheClass(Map<String, File> mapRest, ExecutorService executor)
    {
        Map<String, List<PromptVo>> map = new HashMap<>();
        List<CompletableFuture<Void>> futures = new ArrayList<>();

        for (Map.Entry<String, File> entry : mapRest.entrySet())
        {
            File file = entry.getValue();
            String pageName = file.getName();
            if (file.listFiles() == null)
            {
                continue;
            }
            for (File fil1 : Objects.requireNonNull(file.listFiles()))
            {
                if (fil1.isDirectory())
                {
                    analyzeTheClassChild(fil1, pageName, map, executor, futures);
                }
                else
                {
                    getPromptClass(executor, fil1, futures, map, pageName);
                }
            }
        }

        futures.forEach(CompletableFuture::join);
        return map;
    }

    private static void getPromptClass(ExecutorService executor, File fil1, List<CompletableFuture<Void>> futures, Map<String, List<PromptVo>> map, String pageName )
    {
        String content = readFileText(fil1.getAbsolutePath()) + classCodePrompt;
        if (content.length() > 3000000)
        {
            futures.add(CompletableFuture.runAsync(() ->
                                                   {
                                                       map.computeIfAbsent(pageName, k -> new ArrayList<>())
                                                               .add(PromptVo.builder()
                                                                            .file(fil1)
                                                                            .path(toPack(fil1.getAbsolutePath()))
                                                                            .fileName(fil1.getName())
                                                                            .prompt(content)
                                                                            .resultPrompt("源代码单文件太长暂无解析~")
                                                                            .build());
                                                   }, executor));
        }
        else
        {
            futures.add(CompletableFuture.supplyAsync(() ->
                                                      {

                                                          TongYiCannel tongYiCannel = new TongYiCannel();
                                                          tongYiCannel.setPrompt(content);
                                                          String rest = null;
                                                          try
                                                          {
                                                              rest = tongYiCannel.call();
                                                          } catch (Exception e)
                                                          {
                                                              throw new RuntimeException(e);
                                                          }
                                                          map.computeIfAbsent(pageName, k -> new ArrayList<>())
                                                                  .add(PromptVo.builder()
                                                                               .file(fil1)
                                                                               .path(toPack(fil1.getAbsolutePath()))
                                                                               .fileName(fil1.getName())
                                                                               .prompt(content)
                                                                               .resultPrompt(rest)
                                                                               .build());

                                                          return null;
                                                      }, executor));
        }
    }

    @SneakyThrows
    public static String readFileText(String filePath)
    {
        StringBuffer contentBuilder = new StringBuffer();

        try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
            String line;
            while ((line = reader.readLine()) != null) {
                contentBuilder.append(line).append("\n");
            }
        }

        return contentBuilder.toString().trim();
    }

    /**
     * 把路径转成包的路径
     * @param absolutePath
     * @return
     */
    private static String toPack(String absolutePath) {
        return absolutePath.split("src.main.java.")[1];
    }

    private void analyzeTheClassChild(File fil1, String pageName, Map<String, List<PromptVo>> map, ExecutorService executor, List<CompletableFuture<Void>> futures)
    {
        for (File file : Objects.requireNonNull(fil1.listFiles()))
        {
            if (file.isDirectory())
            {
                analyzeTheClassChild(file, pageName, map, executor, futures);
            }else{
                getPromptClass(executor, file, futures, map, pageName);
            }
        }
    }


    /**
     * 分析配置文件
     *
     * @param file
     * @param executor
     * @return
     * @throws InterruptedException
     * @throws ExecutionException
     */
    private static List<PromptVo> analyzeTheProfile(File file, ExecutorService executor)
    {
        File[] files = file.listFiles();
        List<CompletableFuture<PromptVo>> futures = new ArrayList<>();
        if (ArrayUtil.isNotEmpty(files))
        {
            futures.addAll(Arrays.stream(files)
                                   .filter(File::isFile)
                                   .map(file1 ->
                                        {
                                            String read = readFileText(file1.getAbsolutePath()) + resourcesPrompt;
                                            TongYiCannel tongYiCannel = new TongYiCannel();
                                            tongYiCannel.setPrompt(read);
                                            return CompletableFuture.supplyAsync(() ->
                                                                                 {
                                                                                     String rest = null;
                                                                                     try
                                                                                     {
                                                                                         rest = tongYiCannel.call();
                                                                                     } catch (Exception e)
                                                                                     {
                                                                                         throw new RuntimeException(e);
                                                                                     }
                                                                                     return PromptVo.builder()
                                                                                             .file(file1)
                                                                                             .path(file1.getAbsolutePath())
                                                                                             .fileName(file1.getName())
                                                                                             .prompt(read)
                                                                                             .resultPrompt(rest)
                                                                                             .build();
                                                                                 }, executor);
                                        })
                                   .collect(Collectors.toList()));
        }

        return futures.stream()
                .map(CompletableFuture::join)
                .collect(Collectors.toList());
    }

}
